package com.lcifn.middleware.rpc.transport;

import com.lcifn.middleware.rpc.server.handler.RequestHandlerRegistry;
import com.lcifn.middleware.rpc.transport.coder.ServerRequestDecoder;
import com.lcifn.middleware.rpc.transport.coder.ServerResponseEncoder;
import com.lcifn.middleware.rpc.transport.invocation.ServerRequestInvocation;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 传输服务端netty实现
 * @author lichao
 * @date 2021/1/6 17:25
 */
public class NettyTransportServer implements TransportServer {
    private static final Logger logger = LoggerFactory.getLogger(NettyTransportServer.class);
    // 服务端口
    private int port;
    // 事件接收线程组
    private EventLoopGroup acceptEventGroup;
    // 事件执行线程组
    private EventLoopGroup ioEventGroup;
    // 请求Channel
    private Channel channel;
    // 请求处理器注册中心
    // private RequestHandlerRegistry requestHandlerRegistry;


    /**
     * 服务启动
     *
     * @param port
     */
    @Override
    public void start(int port) throws Exception {
        this.port = port;

        EventLoopGroup acceptEventGroup = newEventLoopGroup();
        EventLoopGroup ioEventGroup = newEventLoopGroup();
        ChannelHandler channelHandlerPipeline = newChannelPipelineHandler();
        ServerBootstrap bootstrap = newServerBootstrap(channelHandlerPipeline, acceptEventGroup, ioEventGroup);
        Channel channel = doBind(bootstrap);

        this.acceptEventGroup = acceptEventGroup;
        this.ioEventGroup = ioEventGroup;
        this.channel = channel;
    }

    /**
     * 服务停止
     */
    @Override
    public void stop() {
        if (this.acceptEventGroup != null) {
            acceptEventGroup.shutdownGracefully();
        }
        if (this.ioEventGroup != null) {
            ioEventGroup.shutdownGracefully();
        }
        if (this.channel != null) {
            channel.close();
        }
    }

    /**
     * 创建ServerBootstrap
     * @return
     */
    private ServerBootstrap newServerBootstrap(ChannelHandler channelHandler, EventLoopGroup acceptEventGroup,
                                               EventLoopGroup ioEventGroup) {
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(acceptEventGroup, ioEventGroup)
                .channel(getServerSocketChannelClass())
                .childHandler(newChannelPipelineHandler());
        return bootstrap;
    }

    /**
     * 服务绑定端口
     * @param bootstrap
     * @return
     * @throws Exception
     */
    private Channel doBind(ServerBootstrap bootstrap) throws Exception {
        return bootstrap.bind(port)
                .sync()
                .channel();
    }

    /**
     * 获取ChannelHandler
     * @return
     */
    private ChannelHandler newChannelPipelineHandler() {
        return new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) {
                ch.pipeline()
                        .addLast(new ServerRequestDecoder())
                        .addLast(new ServerResponseEncoder())
                        .addLast(new ServerRequestInvocation(RequestHandlerRegistry.getInstance()));
            }
        };
    }

    /**
     * 获取ServerSocketChannel
     * @return
     */
    private Class<? extends ServerChannel> getServerSocketChannelClass() {
        return Epoll.isAvailable() ? EpollServerSocketChannel.class : NioServerSocketChannel.class;
    }

    /**
     * 创建事件处理线程组
     * @return
     */
    private EventLoopGroup newEventLoopGroup() {
        if (Epoll.isAvailable()) {
            return new EpollEventLoopGroup();
        }
        return new NioEventLoopGroup();
    }


}
